一、u-boot工程
    1、BootLoader介绍
        BootLoader是操作系统运行之前要执行的一段程序，它负责初始化硬件设备、建立内容空间映射，从而操作系统的运行做好准备，是一个专门加载操作系统的程序。
        对于嵌入式系统而言，没有通用的硬件平台，因此也没有通用的BootLoader，不同的平台、不同的CPU构架都有不同的BootLoader，因为BootLoader不光依赖CPU的体系结构也依赖硬件平台的配置，对于不同的开发板而言，那怕它的CPU一样，而BootLoader都会有区别，因此我们要为每一款开发板制作它属于它的BootLoader程序。
        但大部分开发板的BootLoader仍有许多共性，所以我们没有必须全部从零制作，而对一个基础的BootLoader进行修改，而制作出能用的BootLoader程序。
    2、u-boot简介
        u-boot（ Universal Boot Loader）是BootLoader的一种，它遵循GPL通用许可证的开源项目，它的源码的目录、编译形式都和Linux的源码很相似，可以说u-boot就是仿照Linux而开发的。
    3、u-boot源码的获取
        1、源头的代码是u-boot官网下载的，这种源码是最干净最纯粹的，一般CPU的生产厂商会下载这种源码，当CPU厂商生产出一款CPU之后就会使用这款CPU制作出一块公板(它会把这款CPU所具备的功能全部体现出来)，然后会根据公板修改出一份符合它的u-boot。
        2、开发板的供应商会购买CPU厂商的公板，然后对公板进行裁剪（去掉一些不需要的功能、替换掉一些不必要的硬件），然后再根据裁剪后开板有厂商提供的u-boot进行修改，从而制作出属于这款开发板的u-boot。
        3、官网、CPU厂商、开发板厂商处可以获取u-boot源码，而从开发板厂商处获取的u-boot源码基本上是可以使用的，不需要做什么大的修改。 
    4、u-boot源码结构
        顶层的u-boot源有30多个目录，大致分为三类：
            1、与CPU体系结构或开发板硬件直接相关的代码。
            2、能用的函数、驱动程序
            3、应用程序、工具、文档
        board 平台相关 存储电路板相关的目录文件
        cpu 平台相关 与CPU的体系结构相关的库文件，由于S5PC11x的CPU早于S5PV210,而还兼容S5PV210，所以在这个目录只能看到S5PC110。
        include 通用 u-boot通用的一些头文件，还有一些各种硬件平台的汇编文件、系统配置和支持文件系统，configs目录有与开发板配置相关的头文件。
        common 通用 里面有u-boot所有支持的命令，每一个文件就代表一个命令。
        lib_generic 通用 里面是通用的库函数。
        net 通用 里面是与网络协议相关的代码。
        fs 通用 所支持的文件系统（管理硬盘上的文件的程序）。
        drivers 通用 所支持的设备驱动，如：网卡、串口、USB。
        disk 通用 对磁盘的支持。
        doc 文档 里面非常完整的u-boot说明文档。
        tools 通用 u-boot的一些工具
        examples 案例 一些能够独立运行的应用程序。
        
    5、u-boot的配置编译
        u-boot是通过Makefile组织编译的，顶层的Makefile可以对开发板进行整体配置，然后递归调用各级目录下的Makefile，最后把所有编译过的代码链接成u-boot镜像。
        注意：编译前确定交叉编译，Makefile的147行。
        1、清理之前的编译的残留。
            make distclean
        2、配置编译方法
            make x210_sd_config
            注意：chmod +x mkconfig
        3、多线程编译
            make -jn 一般n是CPU内核数量的2倍。
    6、u-boot的运行
        1、u-boot是ELF格式的，而u-boot.bin才是纯二进制指令格式的。
        2、u-boot.bin并不能直接运行，原因是没有添加校验和，而且之前添加校和的工具不能继续使用。
        3、sd_fusing目录中有为u-boot.bin添加校验和的工具，但并不能直接使用，需要在Makefile添加以下指令。
            291 all:
                cd sd_fusing && make
                ./sd_fusing/mkbl1 u-boot.bin sd-bl1-8k.bin 8192
                dd if=sd-bl1-8k.bin of=u-boot_image.bin
                dd if=u-boot.bin of=u-boot_image.bin seek=48
                rm -rf sd-bl1-8k.bin
                cp u-boot_image.bin /media/sf_shared/image/
                
二、u-boot常用命令
    1、帮助命令
        help/p/?
    2、环境变量相关命令
        printenv 显示所有环境变量
        setenv 添加/删除/修改环境变量
        saveenv 保存环境变量到硬盘
        注意：环境变量都是字符串，没有数据类型之分。
        注意：修改只是临时有效，重启后就会还原，必须使用保存命令才能长期有效。
        u-boot中的环境变量
        netmask：子网掩码
        ipaddr：板子的ip地址
        ethaddr：mac地址
        baudrate：串口波特率
        bootdelay：启动系统前的等待秒数
        gatewayip：网关ip地址
        bootcmd：默认启动时执行的命令
        bootargs：启动时传递给内核的参数
        serverip：服务器ip地址

    3、网络相关的命令
        ping ip 测试与服务器是连通
        tftp 0x01020304 file.bin 可以从服务器下载程序到指定的内存地址。
            1、ubuntu系统需要搭建tftp服务器
            2、在ubuntu系统关机的情况下，设置网络模式为桥接模式，然后启动。
            3、设置ubuntu的ip地址，保证ubuntu、windows、开发板三者ip地址在同一网段、子网掩码一致。
            4、开发板ping通windows系统。
            5、开发板ping通ubuntu系统。
            6、在u-boot中执行 tftp d0020010 led.bin，可以将程序下载到开发板的d0020010地址处，go d0020010 可以到此地址执行。
            注意：Windows防火墙，如果不会配置规则，建议关闭。
    4、启动命令
        boottm 0x01020304 从该内存地址启动系统。
        boot 它需要与bootcmd环境变量配合，根据bootcmd的设置来启动系统。
        
        

        
    
一、u-boot编译过程分析
    u-boot顶层目录下的Makefile负责u-boot的整体编译过程，因此想要了解u-boot的编译过程需要阅读Makefile文件。
    Makefile中没有数据类型之分，全部都是字符串数据。
    具体分析过程参见 Makefile_bck 文件

    总结：
        1、u-boot的源码不是一行行代码写出来的，而拼凑出来的，一份u-boot源码中可能包含了适用于各种开发板的源码，用条件编译进行区分的。
        2、很多文件不自带的，而是配置过程根据一些原材料生成的。
        3、u-boot的整体的代码构架是参考了Linux内核，而编译过程与Linux也很相似。

二、u-boot的配置过程
    1、执行make x210_sd_config 命令，然后make会执行Makefile脚本中的x210_sd_config目标。
    2、x210_sd_config依赖了unconfig，unconfig中会删除所有旧的头文件、mk文件。
    3、然后调用了mkconfig脚本，并传递了6个参数，分别是x210_sd arm s5pc11x x210      samsung s5pc110。
    4、在mkconfig脚本中先确定的开发板的名字，检查参数的数量。
    5、根据参数3，删除旧的链接文件，创建新的链接文件。
    6、然后把参数2、3、4、5、5写到include目录下的config.mk中
    7、在include目录下创建config.h头文件，并让它实际指向，config/下的以参数1命名的头文件。
    8、把0xc3e00000写入board/samsung/x210/config.mk文件中，用于指定链接地址。

三、u-boot的链接脚本
    u-boot的链接脚本在board/samsung/x210/u-boot.lds，与裸机课程中讲的链接脚本没有本质区别，只是复杂度高一些，文件多一些，使用的技巧多一些。
    1、ENTRY(_start) 指定整个uboot的入口地址，类似于C语言中的main函数。
    2、指定程序链接地址的方法有两种
        在Makefile文件中通过参数 -Ttext=0xc3e00000
        在链接脚本中的SECTIONS里 .=0x00000000
    注意：如果两个方式都设置，优先使用-Ttext设置的地址，而u-boot实际使用的是0xc3e00000。
    
    




一、u-boot第一阶段启动流程
    根据u-boot的配置过程可以找到它的链接脚本，然后根据链接脚本中代码段的排列位置找到u-boot的入口代码应该是cpu/s5pc11x/start.S文件。
    1、start.S
        a、导入了一些头文件，这些头文件都配置过程中生成的链接文件（u-boot不能在windows目录下配置、编译）。
        b、config.h头文件是配置过程中生成了，里面的内容只有一行，#include <configs/x210_sd.h>，所以实际被包含的是configs/x210_sd.h。
        c、x210_sd.h文件中有大量的宏，里面都u-boot所需要的一些参数。
        d、version.h里面只记录一行有效内容，#include "version_autogenerated.h"，version_autogenerated.h是配置过程中自动生成的，里面记录是u-boot的版本号。
        e、asm不是u-boot中的原生目录，而配置过程中生成的软链接，实际指向是asm-arm目录，而最终被包含的应该是asm-arm/proc-armv/domain.h。
        f、regs.h配置过程中生成的，实际被包含的是s5pc110.h。
        g、16个字节的校验和，而裸机课程时mkv210_image.c的功能就添加16个字节的校验和。
        h、_start:函数中就是用指令模拟的异常向量表，而_start依赖了reset函数，因此真正的入口函数是reset。
        i、而reset函数中禁用了外部中断和快速中断，设置cpu为SVC（特权模式）。
        j、接下顺序执行了cpu_init_crit函数，首先禁用了二级缓存，然后设置二级缓存，最后再启动二级缓存。
        k、然后初始化一级缓存、禁用MMU(内存管理单元，负责内存映射)，然后读取启动介绍信息，根据r2寄存器中的值，确定启动介绍，最终会以SD/MMC BOOT方式启动。
        l、设置sram内存中位置为栈指针，然后调用lowlevel_init函数，而此函数在board/samsung/x210/lowlevel_init.S。
    2、lowlevel_init.S
        a、检查复位状态，原因是reset函数被执行的原因有很多，比如：冷上电、热启动、睡眠状态下的唤醒，而它们区别是冷上电需要初始化DDR内存，而其它不用，如果是冷上则继续向下执行，否则调用wake。
        b、关闭看门狗，初始化SRAM相关的GPIO管脚，电源锁定。
        c、判断当前代码在什么位置，如果在DDR内存中则直接开始加载系统，如果在SRAM中则需要初始化时钟、DDR等。
        d、初始化时钟，205行~385行的代码都是初始化时钟系统。
        e、初始化DDR内存，mem_ctrl_asm_init函数不在当前文件中，而是定义在cpu/s5pc11x/s5pc110/cpu_init.S。
        f、初始uart，当完成uart的初始化后，向uart发送一个'O'。
        g、初始化TrustZone，开户系统保护。
        h、关闭基带模拟，到此为止lowlevel_init的工作已经全部完成，向uart发送一个'K'，然后返回start.S中。
        总结lowlevel_init中一共做哪些事情：
            检查复位状态、SRAM相关GPIO管理的初始化，关闭看门狗、电源锁定、初始化时钟、初始化DDR内存、初始化串口关打印'O'，启动TZPC、关闭基带模拟关打开'K'。
    3、Start.S
        a、从lowlevel_init函数中返回，再次锁定电源。
        b、再次设置栈指针，因为之前已经设置过了，但当时DDR内存还没有初始化只能调到到SRAM中，而此时DDR已经完成初始化可以设置到DDR内存中了。
        c、再交判断代码的执行位置，这次判断的目的与之前的不同，这是为了决定是否重定位而判断的，如果此时代码运行在SRAM中，则说当前代码不是完成的u-boot，而初始化工作也已经全部完成，接下来则要把完整的u-boot从SD拷贝到DDR中运行。
        d、确定SD卡的通道号，会根据开发板的拨码开关设置自动在内存中设置相应的值。
        e、确定拷贝源，也就是什么介质中拷贝u-boot，最终会调用mmcsd_boot函数。
        f、mmcsd_boot函数中首先调用了movi_bl2_copy函数，而movi_bl2_copy函数会调用iROM中的拷贝函数。
            u32(*copy_sd_mmc_to_mem)(
                u32 channel, 表示通道号
                u32 start_block, 开始扇区号
                u16 block_size, 要拷贝的扇区数量
                u32 *trg, 拷贝到的目标位置
                u32 init); 保留
        g、重定位完成后则开始执行after_copy函数，开启域访问控制（为MMU做准备）。
        h、接下来设置TTB（内存映射转换表），然后开户MMU。
        物理地址：就是设置在生产时赋予的地址，它是根据在CPU管理上的接线位置决定的，物理地址是硬件编码一旦确定无法修改。
            我们在裸机编程时使用到的寄存器的地址都是物理地址，可以通过查阅芯片手册获取并操作，物理地址的缺点就是不够灵活。
        虚拟地址：就是在物理的软件操作之间加一个转换，这个转换操作就地址映射。
            物理地址到虚拟地址的映射时会建立一个地址映射表，操作硬件时，只需要操作虚拟地址，然后MMU芯片会根据地址映射表自动去操作对应的物理地址，这个过程由MMU芯片负责。
        地址映射的好处：
            1、可以编程更灵活
            2、使用代码的通用性更强
            3、在映射时还可以给虚拟地址设置权限，如：可读、可写、可执行等，提高内存的安全性。
            4、还可以把零散的内存映射成为一整块的内存，x210共有两个内存接入口，DRAM0可以接512M内存实际只接入了256M，DRAM1可以接1024M内存而实际只接入了256M内存，因此DRAM0与DRAM1中间是不连续的，而通过MMU的映入可以把DRAM0与DRAM1合并成一整块512M内存更方便使用。
        i、接下来再次设置栈指针，此次设置是在MMU开启之后，现在已经有了一块连续的512M内存，这次的设置会让内存的使用更安全，更紧凑，更节约内存。
        j、清理bss内存段，如果不清理全局变量和static变量就无法定义、使用。
        k、以后工作完成后则调用start_armboot函数，此函数位于lib_arm/board.c中，是一个C语言的函数，此时会跳转到DDR内存中开始第二阶段的u-boot运行。
        u-boot第一阶段启动流程总结：
            1、构建异常向量表
            2、设置CPU工作模式为特权模式
            3、关闭看门狗
            4、开发板电源锁定
            5、时钟系统初始化
            6、DDR内存初始化
            7、初始化串口并打印OK
            8、重定位
            9、建立映射表并开户MMU
            10、跳转到第二阶段
二、u-boot第二阶段启动流程
    1、从宏观角度分析u-boot第二阶段启动流程应该做什么
        a、第一阶段主要是初始化CPU内部的一些硬件（看门狗、时钟、uart等），然后初始化DDR内存、重定位、开户MMU。
    2、第二阶段就应该初始化CPU外部的一些硬件设备了，如：iNand、网卡、ADC。
    3、u-boot本身所需要的一些事情，如：环境变量、命令。
    4、完成所有的初始化工作后开始倒计时：
        1、等待超时，开始加载操作、加载文件系统，然后就是操作系统启动完成。
        2、按任意键，进入u-boot的命令行，在命令行中中可以执行命令、设置或查看环境变量。
            u-boot的命令行就是个死循环，不停的接收命令、解析命令、执行命令。

三、u-boot源码分析
    1、u-boot的源码不是某个人或某人组织完成的，而是不同的组织完成了不同的工作，最终让u-boot具备引导操作系统的能力。
        u-boot官网负责整体代码的组织架构。
        半导体生产广商，根据CPU的特性对BL1段的代码进行修改，添加。
        开发板的生产广商，根据开发板的外部硬件对BL1、BL2阶段的代码进行修改、添加。
    2、所有在修改u-boot代码时都遵循这样的原则：
        尽量不删除代码，那代码会接下的启动流程也是选择注释掉，而不是删除。
        u-boot为了兼容更多的CPU会做一些对于个别CPU无意义的事情。
        尽量多做不要少做，那怕做一些无意义的事情，也要让操作系统顺利启动。
    3、由于u-boot只负责把操作系统启动起来，因此它不需要效率高、安全、稳定。
    4、由于u-boot的代码构成比较复杂，所以质量不高，因此没有借鉴价值。
    5、但u-boot代码是按照Linux内存的框架设计的，因此我们分析、研究、了解是为Linux内核的学习打开基础。

四、u-boot启动过程特征总结
    1、从代码角度来看，第一阶段主要以汇编代码为主，第二阶段以C代码为主。
    2、从内存角度来看，第一阶段主要运行在SRAM中，第二阶段在主要运行在DDR中。
    3、从硬件角度来看，第二阶段注重的是CPU内部，第二阶段注意的是CPU外部、开发板的内部。